﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.IdentityModel.Tokens;
using Soul.IdentityModel.Stores;
using Soul.IdentityServer.Endpoints;
using Soul.IdentityServer.Models;

namespace Soul.IdentityServer.Hosting
{
    public class IdentityServerBuilder
    {
        private IServiceCollection _services;
        private IdentityServiceOptions _options;

        internal IdentityServerBuilder(IServiceCollection services)
        {
            _services = services;
            _options = new IdentityServiceOptions();
        }

        public IdentityServerBuilder ConfigureOptions(Action<IdentityServiceOptions> configure)
        {
            configure(_options);
            return this;
        }

        public IdentityServerBuilder AddEndpoint<T>(string path)
            where T : EndpointBase
        {
            _services.TryAddTransient<T>();
            if (typeof(T) == typeof(DiscoveryEndpoint) || typeof(T) == typeof(DiscoveryJwksEndpoint))
            {
                _services.AddSingleton(new EndpointDescriptor($"{path}", typeof(T)));
            }
            else
            {
                _services.AddSingleton(new EndpointDescriptor($"{_options.EndpointPathPrefix}{path}", typeof(T)));
            }
            return this;
        }

        public IdentityServerBuilder AddClaimService<T>()
            where T : class, IClaimService
        {
            _services.TryAddTransient<IClaimService, T>();
            return this;
        }

        public IdentityServerBuilder AddScopeValidator<T>()
            where T : class, IScopeValidator
        {
            _services.TryAddTransient<IScopeValidator, T>();
            return this;
        }

        public IdentityServerBuilder AddSecretValidator<T>()
         where T : class, IClientSecretValidator
        {
            _services.TryAddTransient<IClientSecretValidator, T>();
            return this;
        }

        public IdentityServerBuilder AddGrantValidator<T>()
            where T : class, IGrantValidator
        {
            _services.AddTransient<IGrantValidator, T>();
            return this;
        }

        public IdentityServerBuilder AddRefreshTokenGrantValidator()
        {
            AddGrantValidator<RefreshTokenGrantValidator>();
            return this;
        }

        public IdentityServerBuilder AddClientCredentialsGrantValidator()
        {
            AddGrantValidator<ClientCredentialsGrantValidator>();
            return this;
        }

        public IdentityServerBuilder AddAuthorizationCodeGrantValidator()
        {
            AddGrantValidator<AuthorizationCodeGrantValidator>();
            return this;
        }

        public IdentityServerBuilder AddClientStore<T>()
            where T : class, IClientStore
        {
            _services.TryAddTransient<IClientStore, T>();
            return this;
        }

       

        public IdentityServerBuilder AddResourceStore<T>()
            where T : class, IResourceStore
        {
            _services.TryAddTransient<IResourceStore, T>();
            return this;
        }

        public IdentityServerBuilder AddSigningCredentialsStore<T>()
             where T : class, ISigningCredentialsStore
        {
            _services.TryAddTransient<ISigningCredentialsStore, T>();
            return this;
        }

        public IdentityServerBuilder AddClientsInMemoryStore(IEnumerable<Client> clients)
        {
            _services.TryAddTransient<IClientStore>(sp => new ClientStore(clients));
            return this;
        }

        public IdentityServerBuilder AddResourcesInMemoryStore(IEnumerable<Resource> resources)
        {
            _services.TryAddTransient<IResourceStore>(sp => new ResourceStore(resources));
            return this;
        }

        public IdentityServerBuilder AddSigningCredentialsInMemoryStore(Action<SigningCredentialsBuilder> configure)
        {
            var builder = new SigningCredentialsBuilder();
            configure(builder);
            var signingCredentials = builder.Build();
            _services.TryAddTransient<ISigningCredentialsStore>(sp => new SigningCredentialsStore(signingCredentials));
            return this;
        }

        public IdentityServerBuilder AddDeveloperSigningCredentialsInMenoryStore(bool persistKey = true, string? filename = null, string signingAlgorithm = SecurityAlgorithms.RsaSha256)
        {
            AddSigningCredentialsInMemoryStore(c => c.AddDeveloperSigningCredentials(persistKey, filename, signingAlgorithm));
            return this;
        }

        internal IdentityServerBuilder AddRequiredEndpoints()
        {
            AddEndpoint<TokenEndpoint>(OpenIdConnectEndpoints.Token);
            AddEndpoint<DiscoveryEndpoint>(OpenIdConnectEndpoints.Discovery);
            AddEndpoint<DiscoveryJwksEndpoint>(OpenIdConnectEndpoints.DiscoveryJwks);
            return this;
        }

        internal IdentityServerBuilder AddRequiredServices()
        {
            _services.TryAddTransient<IClaimService, ClaimService>();
            _services.TryAddTransient<ITokenService, TokenService>();
            _services.TryAddTransient<IDiscoveryService, DiscoveryService>();
            _services.TryAddTransient<IGrantValidatorFactory, GrantValidatorFactory>();
            return this;
        }

        internal IdentityServerBuilder AddRequiredValidators()
        {
            AddScopeValidator<ScopeValidator>();
            AddSecretValidator<ClientSecretValidator>();
            return this;
        }

        internal IdentityServerBuilder AddPlatformCoreServices()
        {
            this._services.AddHttpContextAccessor();
            _services.TryAddSingleton<ISystemClock, SystemClock>();
            _services.AddSingleton(_options);
            return this;
        }

        internal void Build()
        {
            AddPlatformCoreServices()
            .AddRequiredEndpoints()
            .AddRequiredServices()
            .AddRequiredValidators();
        }
    }
}
